
/** 
Copyright (c) 2010, Sheikh Abdul Wahid Ahmed
Details @ http://extremecodeworld.codeplex.com/license
**/

using System;
using System.Collections;
using System.Data;
using System.Xml;
using EXtremecode.Utils;
using EXtremecode.Common; 

namespace EXtremecode.EntityDefinition
{
	/// <summary>
	/// Summary description for Entity.
	/// </summary>
	public abstract class Entity
	{
		
		private const string COLLECTION="Collection";
		protected static EntityCollection s_entityCollectionCreated=new EntityCollection();
		protected EntityFieldCollection m_entityFieldCollectionChild=new EntityFieldCollection(); 
		protected EntityFieldCollection m_entityFieldCollectionParent=new EntityFieldCollection(); 
		protected EntityFieldCollection m_entityFieldCollection=new EntityFieldCollection();
		protected string m_strEntityName;
		protected string m_strEntityType;
		protected string m_accessorName="";
		public Entity(string name,string type)
		{
			if(s_entityCollectionCreated.Contains(name)) throw new Exception(string.Format("[UE] Entity is already in the public Collection with the name '{0}'",name));
			m_strEntityName=name;
			m_strEntityType=type; 
		}
		public static XmlElement GenerateXml(XmlDocument ownerDocument,bool bRefresh)
		{
			XmlElement element;
			XmlNamespaceManager manager=new XmlNamespaceManager(ownerDocument.NameTable);
			manager.AddNamespace("xyz",XMLSpecific.XMLN); 
			XmlNodeList nodes=ownerDocument.GetElementsByTagName("entities",XMLSpecific.XMLN);
			if(nodes.Count>0)
				element=(XmlElement)nodes[0];
			else
				element= ownerDocument.CreateElement(XMLSpecific.PREFIX,"entities",XMLSpecific.XMLN);
			
			foreach(object obj in s_entityCollectionCreated.Keys)
			{
				Entity entity=s_entityCollectionCreated[obj.ToString()];
				XmlNode node=element.SelectSingleNode(string.Format("xyz:entity[@name='{0}']",entity.Name),manager);
				if(bRefresh)
				{
					if(node != null)
					{
						element.RemoveChild(node);
					}
				}
				if(node == null || bRefresh)
					element.AppendChild(entity.BuildXml(ownerDocument));
			}
			return element;
			

		}
		
		public static void ConvertToCustomXml(XmlElement rootElement)
		{
			if(rootElement == null ) return;
			XmlNamespaceManager manager=new XmlNamespaceManager(rootElement.OwnerDocument.NameTable);
			manager.AddNamespace("xyz",XMLSpecific.XMLN);
			XmlNodeList nodes;
			XmlDocument ownerDocument=rootElement.OwnerDocument;
			
			
			//*****Entity
			nodes=rootElement.SelectNodes("xyz:entity[@name]",manager);
			foreach(XmlNode node in nodes)
			{
				
				string strEntityName = node.Attributes["name"].InnerText;
				XmlElement element=node as XmlElement;
				XmlElement fieldsElement=
					element.SelectSingleNode("xyz:fields",manager)as XmlElement;
				element.RemoveAll();
				element.AppendChild(fieldsElement);
				XmlAttribute atb;	
				

				//----name---
				atb= ownerDocument.CreateAttribute("name");
				atb.InnerText=strEntityName;
				element.Attributes.Append(atb);

				//----caption---
				atb= ownerDocument.CreateAttribute("caption");
				atb.InnerText=StringProcessor.Caption(strEntityName);
				element.Attributes.Append(atb);


			}

			//*****Entity Fields
			nodes=rootElement.SelectNodes("xyz:entity/xyz:fields/xyz:field[@name]",manager);
			foreach(XmlNode node in nodes)
			{
				string strEntityFieldName =  node.Attributes["name"].InnerText;
				XmlElement element=(XmlElement)node;
				element.RemoveAllAttributes();
				XmlAttribute atb;
			
			
				//----name---
				atb= ownerDocument.CreateAttribute("name");
				atb.InnerText=strEntityFieldName;
				element.Attributes.Append(atb);


				//----caption---
				atb= ownerDocument.CreateAttribute("caption");
				atb.InnerText=StringProcessor.Caption(strEntityFieldName);
				element.Attributes.Append(atb);

			 
			}

			


		}

		public static void ExtendXml(XmlElement rootElement)
		{
			if(rootElement == null ) return;
			XmlNamespaceManager manager=new XmlNamespaceManager(rootElement.OwnerDocument.NameTable);
			manager.AddNamespace("xyz",XMLSpecific.XMLN);
			XmlNodeList nodes;
			XmlDocument ownerDocument=rootElement.OwnerDocument;
			
			//Entity Name
			nodes=rootElement.SelectNodes("xyz:entity[@name]",manager);
			foreach(XmlNode node in nodes)
			{
				
				string strEntityName = node.Attributes["name"].InnerText;
				XmlElement element=node as XmlElement;
				XmlAttribute atb;	
			
			
				//----className---
				atb= ownerDocument.CreateAttribute("className");
				atb.InnerText=StringProcessor.Class(strEntityName);
				element.Attributes.Append(atb);

				//----variable---
				atb= ownerDocument.CreateAttribute("variable");
				atb.InnerText=StringProcessor.Variable(strEntityName);
				element.Attributes.Append(atb);

				//----caption---
				atb= ownerDocument.CreateAttribute("caption");
				atb.InnerText=StringProcessor.Caption(strEntityName);
				element.Attributes.Append(atb);

			
				//----collection---
				atb= ownerDocument.CreateAttribute("collection");
				atb.InnerText=StringProcessor.Class(strEntityName)+COLLECTION;
				element.Attributes.Append(atb);

				//----tableName---
				atb= ownerDocument.CreateAttribute("tableName");
				atb.InnerText=strEntityName; 
				if(strEntityName.IndexOf('-') > 0)
				{
					atb.InnerText=strEntityName.Substring(strEntityName.IndexOf('-')+1);
				}
				element.Attributes.Append(atb);

				//---View----
				if(element.Attributes["view"]== null)
				{
					atb= ownerDocument.CreateAttribute("view");
					atb.InnerText=strEntityName;
					element.Attributes.Append(atb);
				}
				if(strEntityName.IndexOf('-') > 0)
				{
					atb.InnerText=atb.InnerText.Substring(atb.InnerText.IndexOf('-')+1);
				}




			

			 
			}

			//Entity Key Signature
			nodes=rootElement.SelectNodes("xyz:entity[@name]",manager);
			foreach(XmlNode node in nodes)
			{
				
				string strEntityName = node.Attributes["name"].InnerText;
				XmlElement element=node as XmlElement;
				XmlNodeList nodeFields=element.SelectNodes("xyz:fields/xyz:field[@key='true']",manager);
				string[] strsTypeVarPair=new string[nodeFields.Count]; 
				for(int i=0; i<nodeFields.Count; i++)
				{
					
					XmlNode nodeField=nodeFields[i];
					string strFieldVar =StringProcessor.Variable(nodeField.Attributes["name"].InnerText);
					string strFieldDataType =FreeHash.CSharpType[nodeField.Attributes["datatype"].InnerText];
					strsTypeVarPair[i]=string.Format("{0} {1}",strFieldDataType,strFieldVar); 
				}

				XmlAttribute atb;	
				//----keySignature---
				atb= ownerDocument.CreateAttribute("keySignature");
				Array.Sort(strsTypeVarPair,new ParamComparer());
				atb.InnerText=string.Join(",",strsTypeVarPair).Trim(',');
				element.Attributes.Append(atb);
			

			 
			}
			
			//Entity Field Name
			nodes=rootElement.SelectNodes("xyz:entity/xyz:fields/xyz:field[@name]",manager);
			foreach(XmlNode node in nodes)
			{
				string strEntityFieldName =  node.Attributes["name"].InnerText;
				XmlElement element=(XmlElement)node;
				XmlAttribute atb;
			
			
				//----property---
				atb= ownerDocument.CreateAttribute("property");
				atb.InnerText=StringProcessor.Property(strEntityFieldName);
				element.Attributes.Append(atb);

				//----dbParameter---
				atb= ownerDocument.CreateAttribute("dbParameter");
				atb.InnerText=StringProcessor.DBParameter(strEntityFieldName);
				element.Attributes.Append(atb);

			
				//----variable---
				atb= ownerDocument.CreateAttribute("variable");
				atb.InnerText=StringProcessor.Variable(strEntityFieldName);
				element.Attributes.Append(atb);

				//----caption---
				atb= ownerDocument.CreateAttribute("caption");
				atb.InnerText=StringProcessor.Caption(strEntityFieldName);
				element.Attributes.Append(atb);

				//----Constant---
				atb= ownerDocument.CreateAttribute("constant");
				atb.InnerText=StringProcessor.ConstantNStatic(strEntityFieldName);
				element.Attributes.Append(atb);

				//---Native---
				XmlAttribute  atbType=element.Attributes["type"];
				if(atbType != null)
				{
					atb= ownerDocument.CreateAttribute("isNative");
					bool isNative=false;
					if(atbType.Value.ToString()=="native")
					{
						isNative=true;
					}
					atb.InnerText=isNative.ToString().ToLower();
					element.Attributes.Append(atb);

				}
			 
			}


			//Entity Field DataType		
			nodes=rootElement.SelectNodes("xyz:entity/xyz:fields/xyz:field[@datatype]",manager);
			foreach(XmlNode node in nodes)
			{
				string strDataType = node.Attributes["datatype"].InnerText;
				bool isRange=false;
				XmlElement element=(XmlElement)node;
				XmlAttribute atb;
				Type type=Type.GetType(string.Format("System.{0}",FreeHash.CSharpSystemType[strDataType]));
				isRange=type.IsValueType;
				
			

				//----isRange---
				atb= ownerDocument.CreateAttribute("isRange");
				atb.InnerText=isRange.ToString().ToLower();
				element.Attributes.Append(atb);

				//----cSharpType---
				atb= ownerDocument.CreateAttribute("cSharpType");
				atb.InnerText=FreeHash.CSharpType[strDataType];
				element.Attributes.Append(atb);

				
				//----cSharpSystemType---
				atb= ownerDocument.CreateAttribute("cSharpSystemType");
				atb.InnerText=type.FullName;
				element.Attributes.Append(atb);

		

			 
			}

			//Child Relations Name
			nodes=rootElement.SelectNodes("xyz:entity/xyz:children/xyz:entityReference[@name]",manager);
			foreach(XmlNode node in nodes)
			{
				string strEntityReferenceName =  node.Attributes["name"].InnerText;
				string strEntityNativeName =  node.ParentNode.ParentNode.Attributes["name"].InnerText;
				XmlAttribute atb;
				XmlElement element=(XmlElement)node;
				//----relationName---
				atb= ownerDocument.CreateAttribute("relationName");
				atb.InnerText=StringProcessor.RelationName(strEntityNativeName,strEntityReferenceName);
				element.Attributes.Append(atb);

				//----relationProperty---
				atb= ownerDocument.CreateAttribute("relationProperty");
				atb.InnerText=StringProcessor.RelationProperty(strEntityNativeName,strEntityReferenceName);
				element.Attributes.Append(atb);
			
			}

			//Parent Relations Name
			nodes=rootElement.SelectNodes("xyz:entity/xyz:parents/xyz:entityReference[@name]",manager);
			foreach(XmlNode node in nodes)
			{
				string strEntityReferenceName =  node.Attributes["name"].InnerText;
				string strEntityNativeName =  node.ParentNode.ParentNode.Attributes["name"].InnerText;
				XmlAttribute atb;
				XmlElement element=(XmlElement)node;
				//----relationName---
				atb= ownerDocument.CreateAttribute("relationName");
				atb.InnerText=StringProcessor.RelationName(strEntityReferenceName,strEntityNativeName);
				element.Attributes.Append(atb);

				//----relationProperty---
				atb= ownerDocument.CreateAttribute("relationProperty");
				atb.InnerText=StringProcessor.RelationProperty(strEntityReferenceName,strEntityNativeName);
				element.Attributes.Append(atb);
			
			}


			//Relations 
			nodes=rootElement.SelectNodes("xyz:entity/*/xyz:entityReference[@name]",manager);
			foreach(XmlNode node in nodes)
			{
				//----referenceClass---
				string strEntityReferenceName =  node.Attributes["name"].InnerText;
				XmlElement element=(XmlElement)node;
				XmlAttribute atb;
				atb= ownerDocument.CreateAttribute("referenceClass");
				atb.InnerText=StringProcessor.Class(strEntityReferenceName);
				element.Attributes.Append(atb);

				//----referenceCollection---
				atb= ownerDocument.CreateAttribute("referenceCollection");
				atb.InnerText=StringProcessor.Class(strEntityReferenceName)+COLLECTION;
				element.Attributes.Append(atb);



				//----nativeClass---
				string strEntityNativeName =  node.ParentNode.ParentNode.Attributes["name"].InnerText;
				atb= ownerDocument.CreateAttribute("nativeClass");
				atb.InnerText=StringProcessor.Class(strEntityNativeName);
				element.Attributes.Append(atb);

				
				//----nativeCollection---
				atb= ownerDocument.CreateAttribute("nativeCollection");
				atb.InnerText=StringProcessor.Class(strEntityNativeName)+COLLECTION;
				element.Attributes.Append(atb);


			
			}


			//Relational Fields
			nodes=rootElement.SelectNodes("xyz:entity/*/xyz:entityReference/xyz:relationalFields",manager);
			foreach(XmlNode node in nodes)
			{

				XmlAttribute atb;

				//----referenceFieldProperty---
				string strEntityReferenceFieldName =  node.Attributes["referenceField"].InnerText;
				XmlElement element=(XmlElement)node;
				atb= ownerDocument.CreateAttribute("referenceFieldProperty");
				atb.InnerText=StringProcessor.Property(strEntityReferenceFieldName);
				element.Attributes.Append(atb);

				
				//----nativeFieldProperty---
				string strEntityNativeFieldName =  node.Attributes["nativeField"].InnerText;
				element=(XmlElement)node;
				atb= ownerDocument.CreateAttribute("nativeFieldProperty");
				atb.InnerText=StringProcessor.Property(strEntityNativeFieldName);
				element.Attributes.Append(atb);


			
			}


		}
		private class ParamComparer : IComparer  
		{

			int IComparer.Compare( Object x, Object y )  
			{
				string strX,strY;
				strX=x.ToString().Trim();
				strY=y.ToString().Trim();
				strX=strX.Substring(strX.IndexOf(' ')).Trim();
				strY=strY.Substring(strY.IndexOf(' ')).Trim();
				return( (new CaseInsensitiveComparer()).Compare( strX, strY ) );
			}

		}

		public static void Reset()
		{
			s_entityCollectionCreated.Clear();
		}

		public void AddChild(EntityField entityFieldReference,string strNativeColumn)
		{
			if(!m_entityFieldCollectionChild.Contains(entityFieldReference.Entity.Name+strNativeColumn)) 
				m_entityFieldCollectionChild.Add(entityFieldReference.Entity.Name+strNativeColumn,entityFieldReference);
		}
		
		public void AddParent(EntityField entityFieldReference,string strNativeColumn)
		{
			if(!m_entityFieldCollectionParent.Contains(entityFieldReference.Entity.Name+strNativeColumn)) 
				m_entityFieldCollectionParent.Add(entityFieldReference.Entity.Name+strNativeColumn,entityFieldReference);
		}
		public void AddField(EntityField entityField)
		{
			if(!m_entityFieldCollection.Contains(entityField.Name)) 
				m_entityFieldCollection.Add(entityField.Name,entityField);
		}
		public EntityField this[string fieldName]
		{
			get
			{
				return m_entityFieldCollection[fieldName];
			}
		}
		public virtual XmlElement BuildXml(XmlDocument ownerDocument)
		{
			
			
			XmlElement element= ownerDocument.CreateElement(XMLSpecific.PREFIX,"entity",XMLSpecific.XMLN);
			XmlAttribute atb;
			
			//----name---
			atb= ownerDocument.CreateAttribute("name");
			atb.InnerText=m_strEntityName;
			element.Attributes.Append(atb);

			//----accessorName---
			atb= ownerDocument.CreateAttribute("accessorName");
			atb.InnerText=m_accessorName;
			element.Attributes.Append(atb);
			
			
			//----type---
			atb= ownerDocument.CreateAttribute("type");
			atb.InnerText=m_strEntityType;
			element.Attributes.Append(atb);


			//----fields----
			element.AppendChild(GetFieldsElement(ownerDocument));

			//----children-----
			if(m_entityFieldCollectionChild.Count>0)
			{
				
	
				element.AppendChild(GetChildrenElement(ownerDocument));
			}
			//----parents-----
			if(m_entityFieldCollectionParent.Count>0)
			{
				element.AppendChild(GetParentsElement(ownerDocument));
			}

			
			return element;
			
		}
		private XmlElement GetFieldsElement(XmlDocument ownerDocument)
		{
			XmlElement element= ownerDocument.CreateElement(XMLSpecific.PREFIX,"fields",XMLSpecific.XMLN);
			foreach(object obj in m_entityFieldCollection.Keys)
			{
				element.AppendChild(m_entityFieldCollection[(string)obj].BuildXml(ownerDocument)); 
			}
			return element;

		}
		public static XmlElement GetRootElement( XmlDocument ownerDocument)
		{
			
			XmlElement element;
			XmlNamespaceManager manager=new XmlNamespaceManager(ownerDocument.NameTable);
			manager.AddNamespace("xyz",XMLSpecific.XMLN); 
			XmlNodeList nodes=ownerDocument.GetElementsByTagName("entities",XMLSpecific.XMLN);
			if(nodes.Count>0)
				element=(XmlElement)nodes[0];
			else
				element= ownerDocument.CreateElement(XMLSpecific.PREFIX,"entities",XMLSpecific.XMLN);
			return element;
		}

		private XmlElement GetChildrenElement(XmlDocument ownerDocument)
		{
			XmlElement element= ownerDocument.CreateElement(XMLSpecific.PREFIX,"children",XMLSpecific.XMLN);
			XmlNamespaceManager manager=new XmlNamespaceManager(ownerDocument.NameTable);
			manager.AddNamespace("xyz",XMLSpecific.XMLN);

			foreach(object obj in m_entityFieldCollectionChild.Keys)
			{
				EntityField reference= m_entityFieldCollectionChild[(string)obj];
				string nativeField=obj.ToString().Substring(reference.Entity.Name.Length);
				XmlNodeList entityReferenceNodes=element.SelectNodes(string.Format("xyz:entityReference[@name='{0}']",reference.Entity.Name),manager);  
				XmlElement entityReferenceElement=null;
				if(entityReferenceNodes.Count  > 0)
					entityReferenceElement = entityReferenceNodes[0] as XmlElement; 

				if(entityReferenceElement== null)
					element.AppendChild(GetEntityReferenceElement(ownerDocument,nativeField,reference));
				else
					AddElementRelationalFieldsToEntityReferenceElement(ownerDocument,nativeField,reference,entityReferenceElement); 
			}

			
			return element;

		}
		private XmlElement GetParentsElement(XmlDocument ownerDocument)
		{
			XmlNamespaceManager manager=new XmlNamespaceManager(ownerDocument.NameTable);
			manager.AddNamespace("xyz",XMLSpecific.XMLN);

			XmlElement element= ownerDocument.CreateElement(XMLSpecific.PREFIX,"parents",XMLSpecific.XMLN);
			foreach(object obj in m_entityFieldCollectionParent.Keys)
			{
				EntityField reference= m_entityFieldCollectionParent[(string)obj]; 
				string nativeField=obj.ToString().Substring(reference.Entity.Name.Length);

				XmlNodeList entityReferenceNodes=element.SelectNodes(string.Format("xyz:entityReference[@name='{0}']",reference.Entity.Name),manager);  
				XmlElement entityReferenceElement=null;
				if(entityReferenceNodes.Count  > 0)
					entityReferenceElement = entityReferenceNodes[0] as XmlElement; 

				if(entityReferenceElement== null)
					element.AppendChild(GetEntityReferenceElement(ownerDocument,nativeField,reference)); 
				else
					AddElementRelationalFieldsToEntityReferenceElement(ownerDocument,nativeField,reference,entityReferenceElement); 

			}
			return element;

		}
		private XmlElement GetEntityReferenceElement(XmlDocument ownerDocument,string strNative,EntityField entityFieldReference)
		{
			XmlElement element= ownerDocument.CreateElement(XMLSpecific.PREFIX,"entityReference",XMLSpecific.XMLN);
			XmlAttribute atb;


			//----name---
			atb= ownerDocument.CreateAttribute("name");
			atb.InnerText=entityFieldReference.Entity.Name;
			element.Attributes.Append(atb);
			AddElementRelationalFieldsToEntityReferenceElement(ownerDocument,strNative,entityFieldReference,element); 
			

			return element;
		}
		private void  AddElementRelationalFieldsToEntityReferenceElement(XmlDocument ownerDocument,string strNative,EntityField entityFieldReference,XmlElement element)
		{
			XmlElement elementRelationalFields= ownerDocument.CreateElement(XMLSpecific.PREFIX,"relationalFields",XMLSpecific.XMLN);
			XmlAttribute atb;
			
			
			//----referenceField---
			atb= ownerDocument.CreateAttribute("referenceField");
			atb.InnerText=entityFieldReference.Name;
			elementRelationalFields.Attributes.Append(atb);

			
			//----nativeField---
			atb= ownerDocument.CreateAttribute("nativeField");
			atb.InnerText=strNative;
			elementRelationalFields.Attributes.Append(atb);

			element.AppendChild(elementRelationalFields); 

		}
		public string Name
		{
			get
			{
				return m_strEntityName; 
			}
		}
		public static bool IsTableSelectedForEntity(DataTable dt)
		{
			bool bSelected=false;
			if(dt.ExtendedProperties.Contains("Selected"))
			{
				bSelected= (bool)dt.ExtendedProperties["Selected"];
			}
			return bSelected;
		}
		public static void SelectTableForEntity(DataTable dt,bool bSelected)
		{
			if(!dt.ExtendedProperties.Contains("Selected"))
			{
				dt.ExtendedProperties.Add("Selected",bSelected);	
			}
			dt.ExtendedProperties["Selected"]=bSelected; 

		}
		public static DataTable[] GetListOfSelectedTables(XmlElement rootElement,DataSet dsTables)
		{
			if(rootElement == null ) return new DataTable[0];
			System.Collections.ArrayList alTables=new System.Collections.ArrayList();
			XmlNamespaceManager manager=new XmlNamespaceManager(rootElement.OwnerDocument.NameTable);
			manager.AddNamespace("xyz",XMLSpecific.XMLN);
			XmlNodeList nodes;
			XmlDocument ownerDocument=rootElement.OwnerDocument;
			
			//Entity Name
			nodes=rootElement.SelectNodes("xyz:entity[@name]",manager);
			foreach(XmlNode node in nodes)
			{
				
				string strEntityName = node.Attributes["name"].InnerText;
				string dbName = node.Attributes["accessorName"].InnerText;
				if(!dsTables.Tables.Contains(strEntityName) && dsTables.Tables.Contains(dbName+"-"+ strEntityName))
				{
					dsTables.Tables[dbName+"-"+ strEntityName].TableName=strEntityName;	
				}

				DataTable dt=null;
				if(dsTables.Tables.Contains(strEntityName))
				{
					dt=dsTables.Tables[strEntityName];
					alTables.Add(dt);
					SelectTableForEntity(dt,true);
				}
				
			}
			if(alTables.Count>0)
				return (DataTable[])alTables.ToArray(alTables[0].GetType());
			else
				return new DataTable[0];
		}

	}


	public class EntityCollection:Hashtable
	{
		
		
		
		public Entity this[string key]
		{
			get
			{
				return (base[key] as Entity);
			}
			
		}
		public  void Add(string key, Entity value)
		{
			if(base.Contains(key))throw new Exception(string.Format("[UE] Entity with the key '{0}', already exsists",key));
			base.Add(key,value);
		}
		

		
		
		#region Hidden Methods
		public override void Add(object key, object value)
		{
			throw new Exception("[UE] method is not allowed");
		}
		public override void CopyTo(Array array, int arrayIndex)
		{
			throw new Exception("[UE] method is not allowed");

		}
		#endregion
	}
}
